home *** CD-ROM | disk | FTP | other *** search
/ LiquidLibrary 2005 February / LiquidLibrary 2005 February - Disc 1.iso / pc / Portfolio Browser / Filters / PDF / LIB / pdfopt.ps < prev    next >
Text File  |  2003-01-03  |  33KB  |  1,116 lines

  1. %    Copyright (C) 2000, 2001 Aladdin Enterprises.  All rights reserved.
  2. % This software is licensed to a single customer by Artifex Software Inc.
  3. % under the terms of a specific OEM agreement.
  4.  
  5. % $RCSfile$ $Revision$
  6. % PDF linearizer ("optimizer").
  7.  
  8. .currentglobal true .setglobal
  9. /pdfoptdict 200 dict def
  10. pdfoptdict begin
  11.  
  12. % This linearizer is designed for simplicity, not for performance.
  13. % See the main program (the last procedure in the file) for comments
  14. % describing the main processing sequence.
  15.  
  16. % ---------------- Utilities ---------------- %
  17.  
  18. % ------ Data structures ------ %
  19.  
  20. % Distinguish dictionaries, arrays, and everything else.
  21. /ifdaelse {        % <obj> <dictproc> <arrayproc> <otherproc> ifdaelse -
  22.   3 index type dup /dicttype eq {
  23.     pop pop pop
  24.   } {
  25.     dup /arraytype ne exch /packedarraytype ne and {
  26.       exch
  27.     } if pop exch pop
  28.   } ifelse exec
  29. } bind def
  30.  
  31. % Implement dynamically growable arrays using a dictionary.
  32. /darray {        % <size> darray <darray>
  33.   dict
  34. } bind def
  35. /dadd {            % <darray> <value> dadd -
  36.   1 index length exch put
  37. } bind def
  38. /daforall {        % <darray> <proc> daforall -
  39.   /exch cvx /get cvx 3 -1 roll /exec cvx 5 packedarray cvx
  40.   0 1 2 index 0 get length 1 sub 4 -1 roll for
  41. } bind def
  42. /dacontents {        % <darray> dacontents <array>
  43.   [ exch { } daforall ]
  44. } bind def
  45. /dacontstring {        % <darray> dacontstring <string>
  46.   0 1 index { exch pop length add } forall string
  47.   dup /NullEncode filter
  48.             % Stack: darray str filter
  49.   3 -1 roll { 1 index exch writestring } daforall
  50.   closefile
  51. } bind def
  52.  
  53. % Force an object, mapping it if it is a reference.
  54. /omforcenew {        % <obj> omforce <obj'> <notseen>
  55.   dup oforce 2 copy eq { pop true } { exch 0 get omapnew exch pop } ifelse
  56. } bind def
  57. /omforce {        % <obj> omforce <obj'>
  58.   omforcenew pop
  59. } bind def
  60. /omget {        % <dict|array> <key> omget <obj>
  61.   get omforce
  62. } bind def
  63. % Visit an entire tree.
  64. /omvisit {        % <obj> omvisit -
  65.   omforcenew {
  66.     { { omvisit omvisit } forall }
  67.     { { omvisit } forall }
  68.     { pop }
  69.     ifdaelse
  70.   } {
  71.     pop
  72.   } ifelse
  73. } bind def
  74. % Visit a tree, stopping at references to Page objects.
  75. % (This is only needed for the OpenAction in the Catalog.)
  76. /omvisitnopage {    % <obj> omvisitnopage -
  77.   dup oforce dup type /dicttype eq {
  78.     /Type .knownget { /Page eq } { false } ifelse
  79.   } {
  80.     pop false
  81.   } ifelse {
  82.     pop        % Page reference
  83.   } {
  84.     omforcenew {
  85.       { { omvisitnopage omvisitnopage } forall }
  86.       { { omvisitnopage } forall }
  87.       { pop }
  88.       ifdaelse
  89.     } {
  90.       pop
  91.     } ifelse
  92.   } ifelse
  93. } bind def
  94.  
  95. % Collect the list of currently mapped object numbers, in order.
  96. /omapped {        % - omapped <obj#s>
  97.   RMap length array RMap {
  98.     2 index 3 1 roll 1 sub exch put
  99.   } forall
  100. } bind def
  101.  
  102. % Collect the list of object numbers passed to omap by a procedure.
  103. /visited {        % <proc> visited <obj#s>
  104.   false currentomap 2 .execn
  105.   omapped exch setomap
  106. } bind def
  107.  
  108. % ------ Output ------ %
  109.  
  110. % Provide a framework for closure-based streams.
  111. .currentglobal false .setglobal
  112. userdict /clostreams 20 dict put    % stream -> [data endproc]
  113. .setglobal
  114. % Create a closure-based stream.
  115. /clostream {        % <data> <proc> <endproc> clostream <stream>
  116.   2 index 3 -1 roll /exec load 3 packedarray cvx
  117.   /NullEncode filter
  118.         % Stack: data endproc stream
  119.   clostreams 1 index 5 -2 roll 2 array astore put
  120. } bind def
  121. % Close a closure-based stream.
  122. /closend {        % <stream> closend <result>
  123.   dup closefile clostreams exch
  124.   2 copy get 3 1 roll undef aload pop exec
  125. } bind def
  126.  
  127. % Implement in-memory output streams.
  128. /msproc {        % <data> <more> <accum> msproc <scratch>
  129.   3 -1 roll dadd { 100 string } { () } ifelse
  130. } bind def
  131. /mstream {        % - mstream <mstream>
  132.   10 darray {msproc} {dacontstring} clostream
  133. } bind def
  134. /mcontents {        % <mstream> mcontents <string>
  135.   closend
  136. } bind def
  137.  
  138. % Implement a stream that only keeps track of its position.
  139. % (All streams should do this, but the PLRM doesn't require it.)
  140. /posbuf 100 string def
  141. /posproc {        % <data> <more> <accum> posproc <scratch>
  142.   0 2 copy get 5 -1 roll length add put
  143.   pop //posbuf
  144. } bind def
  145. /postream {        % - postream <postream>
  146.   [0] {posproc} {0 get} clostream
  147. } bind def
  148. /poslength {        % <postream> poslength <pos>
  149.   closend
  150. } bind def
  151.  
  152. % Implement streams with variable-bit-width data.
  153. % Note that these are dictionary objects, not stream objects.
  154. /bitstream {        % <stream> bitstream <bstream>
  155.   4 dict begin /S exch def /N 8 def /B 0 def
  156.   currentdict end
  157. } bind def
  158. /bitwrite {        % <bstream> <value> <width> bitwrite -
  159.   PDEBUG { ( ) print 1 index =only (:) print dup = } if
  160.   3 -1 roll begin
  161.   N exch sub dup 0 ge {
  162.     /N exch def N bitshift B add
  163.   } {
  164.     2 copy bitshift B add S exch write
  165.             % Stack: value -left
  166.     { 8 add dup 0 ge { exit } if
  167.       2 copy bitshift 255 and S exch write
  168.     } loop
  169.     /N 1 index def bitshift 255 and
  170.   } ifelse /B exch def
  171.   end
  172. } bind def
  173. /bitflush {        % <bstream> bitflush -
  174.   begin N 8 ne { S B write /B 0 def /N 8 def } if end
  175. } bind def
  176.  
  177. % Capture OFile output on the temporary file, in memory, or just as a length.
  178. /totemp {        % <proc> totemp <start> <end>
  179.   TFile fileposition OFile
  180.   /OFile TFile def 3 .execn
  181.   /OFile exch def
  182.   TFile fileposition
  183. } bind def
  184. /tomemory {        % <proc> tomemory <string>
  185.   OFile /OFile mstream def 2 .execn
  186.   OFile mcontents exch /OFile exch def
  187. } bind def
  188. /tolength {        % <proc> tolength <string>
  189.   OFile /OFile postream def 2 .execn
  190.   OFile poslength exch /OFile exch def
  191. } bind def
  192.  
  193. % Copy a range of bytes from TFile to OFile.
  194. /copyrange {        % <start> <end> copybytes -
  195.   TFile 2 index setfileposition
  196.   exch sub 1024 string exch {
  197.         % Stack: buf left
  198.     2 copy 1 index length .min 0 exch getinterval
  199.     TFile exch readstring pop OFile exch writestring
  200.     1 index length sub dup 0 le { exit } if
  201.   } loop pop pop
  202. } bind def
  203.  
  204. % Pad with blanks to a specified position.
  205. /padto {        % <pos> padto -
  206.   OFile fileposition sub
  207.   dup 0 lt {
  208.     (ERROR: file position incorrect by ) print =
  209.     /padto cvx /rangecheck signalerror
  210.   } {
  211.     { ( ) ows } repeat
  212.   } ifelse
  213. } bind def
  214.  
  215. % ---------------- Read objects into memory ---------------- %
  216.  
  217. /touch {        % <object> touch -
  218.   {
  219.     { touch touch } forall
  220.   } {
  221.     dup xcheck {
  222.         % Executable array, must be an indirect object.
  223.       dup 0 get resolved? { pop pop } { oforce touch } ifelse
  224.     } {
  225.       { touch } forall
  226.     } ifelse
  227.   } {
  228.     pop
  229.   } ifdaelse
  230. } bind def
  231.  
  232. % ---------------- Replace references with referents ---------------- %
  233.  
  234. /replaceable? {        % <value> replaceable? <bool>
  235.   dup type /integertype eq exch xcheck not and
  236. } bind def
  237. /replacement {        % <obj|ref> replacement <obj'>
  238.   dup oforce dup replaceable? { exch } if pop
  239. } bind def
  240.  
  241. /replacerefs {        % <object> replacerefs <object>
  242.   {
  243.     dup {
  244.       2 index 2 index undef
  245.       exch replacement exch replacement
  246.       2 index 3 1 roll put
  247.     } forall
  248.   } {
  249.     0 1 2 index length 1 sub {
  250.       1 index exch 2 copy get replacement put
  251.     } for
  252.   } {
  253.   } ifdaelse
  254. } bind def
  255.  
  256. /replaceReferences {    % - replaceReferences -
  257.   Objects { replacerefs pop } lforall
  258.         % Delete replaced objects.
  259.   0 1 Objects llength 1 sub {
  260.     Objects 1 index lget replaceable? {
  261.       PDEBUG { (Deleting ) print dup = } if
  262.       Generations 1 index 0 lput
  263.     } if pop
  264.   } for
  265. } bind def
  266.  
  267. % ---------------- Create new objects ---------------- %
  268.  
  269. /createObjects {    % [<obj>...] createObjects <firstobj#>
  270.   Objects llength dup
  271.   dup 3 index length add growPDFobjects
  272.         % Stack: objects objn objn
  273.   3 1 roll exch {
  274.     Objects 2 index 3 -1 roll lput
  275.     Generations 1 index 1 lput
  276.     1 add
  277.   } forall pop
  278. } bind def
  279.  
  280. % ---------------- Propagate attributes ---------------- %
  281.  
  282. /nopropattrs <<
  283.     % Never propagate these.
  284.   /Type dup /Kids dup /Count dup /Parent dup
  285.     % Handle Resources specially.
  286.   /Resources dup
  287. >> def
  288.  
  289. % Merge Resources.
  290. /mergeres {        % <fromdict> <todict> mergeres -
  291.         % Values in todict take priority over fromdict.
  292.   1 index /Resources .knownget {
  293.     1 index /Resources .knownget {
  294.         % Stack: fromdict todict fromres tores
  295.       exch oforce exch oforce
  296.         % todict's Resources may be shared, so make a copy.
  297.       dup length dict .copydict
  298.       exch {
  299.         % Stack: fromdict todict tores' fromkey fromvalue
  300.     2 index 2 index knownoget {
  301.         % Stack: fromdict todict tores' fromkey fromvalue tovalue
  302.       exch oforce exch
  303.         % ProcSet is an array, other types are dictionaries.
  304.       dup type /dicttype eq {
  305.         % Dictionary, not ProcSet.
  306.         exch dup length 2 index length add dict .copydict .copydict
  307.       } {
  308.         % Array or packed array, ProcSet.
  309.         % Use dictionaries to do the merge.
  310.         dup length 2 index length add dict begin
  311.         exch { dup def } forall { dup def } forall
  312.         mark currentdict end { pop } forall .packtomark
  313.       } ifelse
  314.     } if
  315.     2 index 3 1 roll put
  316.       } forall
  317.     } if /Resources exch put pop
  318.   } {
  319.     pop pop
  320.   } ifelse
  321. } bind def
  322.  
  323. % Merge attributes other than Resources.
  324. /mergeattrs {        % <fromdict> <todict> mergeattrs <fromdict> <todict>
  325.         % Values in todict take priority over fromdict.
  326.   1 index {
  327.         % Stack: fromdict todict fromkey fromvalue
  328.     //nopropattrs 2 index known {
  329.       pop pop
  330.     } {
  331.       2 index 2 index known { pop pop } { 2 index 3 1 roll put } ifelse
  332.     } ifelse
  333.   } forall
  334. } bind def
  335.  
  336. % Propagate attributes to a subtree.
  337. /proppage {        % <attrs> <subtree> proppage -
  338.         % We should be able to tell when we reach a leaf
  339.         % by finding a Type unequal to /Pages.  Unfortunately,
  340.         % some files distributed by Adobe lack the Type key
  341.         % in some of the Pages nodes!  Instead, we check for Kids.
  342.   dup /Kids knownoget {
  343.         % Accumulate inherited values.
  344.     3 1 roll
  345.         % Stack: kids attrs pagesnode
  346.     dup length dict .copydict mergeattrs
  347.     dup 3 1 roll mergeres
  348.     exch { oforce 1 index exch proppage } forall pop
  349.   } {
  350.         % Merge inherited values into the leaf.
  351.     mergeattrs mergeres
  352.   } ifelse
  353. } bind def
  354.  
  355. % Propagate attributes to all pages.
  356. /propagateAttributes {    % - propagateAttributes -
  357.   0 dict Trailer /Root oget /Pages oget proppage
  358. } bind def
  359.  
  360. % ---------------- Identify document-level objects ---------------- %
  361.  
  362. /identifyDocumentObjects {    % - identifyDocumentObjects <obj#s>
  363.   {
  364.     Trailer /Root omget
  365.     dup /PageMode .knownget { omvisit } if
  366.     % Don't allow omvisit to trace references to Page objects.
  367.     dup /OpenAction .knownget { omvisitnopage } if
  368.     Trailer /Encrypt .knownget { omvisit } if
  369.     dup /Threads .knownget {
  370.       omforce { omforce } forall
  371.     } if
  372.     dup /AcroForm .knownget { omvisit } if
  373.     pop
  374.   } visited
  375. } bind def
  376.  
  377. % ---------------- Identify the objects of each page ---------------- %
  378.  
  379. /identifyfont {        % <fontref> identifyfont -
  380.   omforce {
  381.     exch /FontDescriptor eq {
  382.       omforce dup /Flags .knownget { 32 and 0 ne } { false } ifelse
  383.       exch {
  384.     exch dup dup /FontFile eq exch /FontFile2 eq or
  385.     exch /FontFile3 eq or 2 index and {
  386.       fontfiles exch dadd
  387.     } {
  388.       omvisit
  389.     } ifelse
  390.       } forall pop
  391.     } {
  392.       omvisit
  393.     } ifelse
  394.   } forall
  395. } bind def
  396.  
  397. /identifyPageObjects {    % <extra> <page#> identifyPageObjects <obj#s>
  398.   pdffindpageref
  399.   4 dict begin
  400.   /images 10 darray def
  401.   /fontfiles 10 darray def
  402.   {
  403.     omforce
  404.         % Stack: extra page
  405.         % Visit any extra objects if applicable.
  406.     exch omvisit
  407.         % Visit Annots, if any.
  408.         % We don't try to defer the drawing information.
  409.     dup /Annots .knownget { omvisit } if
  410.         % Visit beads.
  411.     dup /B .knownget { omvisit } if
  412.         % Visit resources dictionaries.
  413.     dup /Resources .knownget {
  414.       omforce dup {
  415.         % Visit the first-level Resource dictionaries.
  416.     omforce pop pop
  417.       } forall {
  418.         % Visit the resources themselves.
  419.         % Skip Image XObjects, and FontFile streams if the
  420.         % FontDescriptor Flags have bit 6 set.
  421.         % We don't try to visit the resources in the order in which
  422.         % the Contents stream(s) reference(s) them.
  423.     exch dup /XObject eq {
  424.       pop oforce {
  425.         dup oforce /Subtype get /Image eq {
  426.           images exch dadd
  427.         } {
  428.           omvisit
  429.         } ifelse pop
  430.       } forall
  431.     } {
  432.       /Font eq {
  433.         oforce { identifyfont pop } forall
  434.       } {
  435.         oforce omvisit
  436.       } ifelse
  437.     } ifelse
  438.       } forall
  439.     } if
  440.         % Visit the Contents stream(s).
  441.     dup /Contents .knownget { omvisit } if
  442.         % Visit Image XObjects.  We don't try to visit them in
  443.         % reference order.
  444.     images { omvisit } daforall
  445.         % Visit FontFile streams.  We don't try to visit them in
  446.         % reference order.
  447.     fontfiles { omvisit } daforall
  448.     pop
  449.   } visited end
  450. } bind def
  451.  
  452. % Identify the objects of the first page.
  453. /identifyFirstPageObjects {    % -identifyFirstPageObjects <obj#s>
  454.   Trailer /Root oget null
  455.   1 index /PageMode knownoget {
  456.     /UseOutlines eq {
  457.       1 index /Outlines knownoget { exch pop } if
  458.     } if
  459.   } if exch pop
  460.   1 identifyPageObjects
  461. } bind def
  462.  
  463. % Identify the non-shared objects of the other pages, and the shared objects.
  464. /identifyOtherPageObjects {    % - identifyOtherPageObjects [<pageobj#s> ...]
  465.                 %   <sharedobj#s>
  466.   4 dict begin
  467.   /marks lstring Objects llength lgrowto def
  468.         % Mark document-level and first page objectsw.
  469.   [CatalogNs FirstPageNs] {
  470.     { marks exch 255 lput } forall
  471.   } forall
  472.         % Collect objects of other pages and identify sharing.
  473.   [ 2 1 pdfpagecount { null exch identifyPageObjects } for ]
  474.   dup {
  475.     { marks exch 2 copy lget 1 add 254 min lput } forall
  476.   } forall
  477.   [ exch {
  478.     [ exch {
  479.       marks 1 index lget 1 ne { pop } if
  480.     } forall ]
  481.   } forall ]
  482.   [ 1 1 marks llength 1 sub {
  483.     marks 1 index lget dup 1 le exch 255 eq or { pop } if
  484.   } for ]
  485.   end
  486. } bind def
  487.  
  488. % Identify objects not associated with any page.
  489. /identifyNonPageObjects {    % - identifyNonPageObjects <obj#s>
  490.   4 dict begin
  491.   /marks lstring Objects llength lgrowto def
  492.   [[[LPDictN PHSN] CatalogNs FirstPageNs SharedNs] OtherPageNs] {
  493.     { { marks exch 1 lput } forall } forall
  494.   } forall
  495.     %****** PUT THESE IN A REASONABLE ORDER ******
  496.   [ 1 1 Objects llength 1 sub {
  497.     marks 1 index lget 0 eq {
  498.       Generations 1 index lget 0 eq { pop } if
  499.     } {
  500.       pop
  501.     } ifelse
  502.   } for ]
  503. } bind def
  504.  
  505. % ---------------- Assign object numbers ---------------- %
  506.  
  507. % Assign object numbers to all objects that will be copied.
  508. % Return the first (translated) object number in the First Page xref table.
  509. /assignObjectNumbers {        % - assignObjectNumbers -
  510.   OtherPageNs { { omap pop } forall } forall
  511.   SharedNs { omap pop } forall
  512.   NonPageNs { omap pop } forall
  513.         % Assign object numbers for the First Page xref table last.
  514.   LPDictN omap    % don't pop, this is the return value
  515.   CatalogNs { omap pop } forall
  516.   FirstPageNs { omap pop } forall
  517.   PHSN omap pop
  518. } bind def
  519.  
  520. % ---------------- Create the LPDict ---------------- %
  521.  
  522. % Create the contents of the LPDict.
  523. /createLPDict {            % <phsstart> <phsend> <firstpageend>
  524.                 %   <xref0start> <filelength> createLPDict -
  525.   LPDict
  526.   dup /Linearized 1 put
  527.   dup /L 4 -1 roll put        % filelength
  528.   dup /T 4 -1 roll put        % xref0start
  529.   dup /E 4 -1 roll put        % firstpageend
  530.   dup /H 5 -2 roll 1 index sub 2 array astore put    % phsstart, end-start
  531.   dup /O 1 pdffindpageref 0 get omap put
  532.   /N pdfpagecount put
  533. } bind def
  534.  
  535. % ---------------- Adjust object positions ---------------- %
  536.  
  537. /adjustObjectPositions {    % <boundary> <deltabelow> <deltaabove>
  538.                 %   adjustObjectPositions -
  539.     % Objects fall into 4 categories: LPDict, PHS, Catalog, and others.
  540.     % We handle the first two as special cases.
  541.   XRef {
  542.         % Stack: bdy below above key loc
  543.     dup 5 index ge { 2 } { 3 } ifelse index add
  544.     XRef 3 1 roll put
  545.   } forall pop pop pop
  546.   XRef LPDictN omap HeaderLength put
  547.   XRef PHSN omap PHSStart put
  548. } bind def
  549.  
  550. % ---------------- Write the output file ---------------- %
  551.  
  552. % Write objects identified by object number.
  553. /writeobjn {        % <obj#> writeobjn -
  554.   Generations 1 index lget pdfwriteobj
  555. } bind def
  556. /writeobjns {        % <obj#s> writeobjns -
  557.   { writeobjn } forall
  558. } bind def
  559.  
  560. % Write a part of the output file.
  561. /writePart {        % <proc> <label> writePart -
  562.   PDEBUG {
  563.     dup print ( start=) print
  564.     OFile { .fileposition } stopped { pop (???) } if =
  565.     2 .execn
  566.     print ( end=) print
  567.     OFile { .fileposition } stopped { pop (???) } if =
  568.   } {
  569.     pop exec
  570.   } ifelse
  571. } bind def
  572.  
  573. % Write the header.
  574. /writePart1 {        % - writePart1 -
  575.   {
  576.     pdfwriteheader
  577.   } (part1) writePart
  578. } bind def
  579.  
  580. % Write the linearization parameters dictionary.
  581. /writePart2 {        % - writePart2 -
  582.   {
  583.     LPDictN writeobjn
  584.   } (part2) writePart
  585. } bind def
  586.  
  587. % Write the First Page xref table and trailer.
  588. % Free variables: FirstPageXN.
  589. /writePart3 {        % <xrefstart> writePart3 -
  590.   {
  591.     FirstPageXN NObjects 1 add 1 index sub pdfwritexref
  592.     Trailer dup length 1 add dict copy
  593.     dup /Size NObjects 1 add put
  594.     dup /Prev 4 -1 roll put
  595.     pdfwritetrailer
  596.     0 pdfwritestartxref
  597.   } (part3) writePart
  598. } bind def
  599.  
  600. % Write the Catalog and other required document-level objects.
  601. % Free variables: CatalogNs.
  602. /writePart4 {        % - writePart4 -
  603.   {
  604.     CatalogNs writeobjns
  605.   } (part4) writePart
  606. } bind def
  607.  
  608. % Write the Primary Hint Stream.
  609. /writePart5 {        % - writePart5 -
  610.   {
  611.     PHSN writeobjn
  612.   } (part5) writePart
  613. } bind def
  614.  
  615. % Write the First Page's objects.
  616. % Free variables: FirstPageNs.
  617. /writePart6 {        % - writePart6 -
  618.   {
  619.     FirstPageNs writeobjns
  620.   } (part6) writePart
  621. } bind def
  622.  
  623. % Write the objects of other pages (Page + non-shared objects).
  624. % Free variables: OtherPageNs.
  625. /writePart7 {        % - writePart7 <lengths>
  626.   {
  627.     [ OtherPageNs {
  628.       OFile fileposition exch
  629.       writeobjns OFile fileposition exch sub
  630.     } forall ]
  631.   } (part7) writePart
  632. } bind def
  633.  
  634. % Write the shared objects of other pages.
  635. % Free variables: SharedNs.
  636. /writePart8 {        % - writePart8 -
  637.   {
  638.     SharedNs writeobjns
  639.   } (part8) writePart
  640. } bind def
  641.  
  642. % Write the other objects not associated with pages.
  643. % Free variables: NonPageNs.
  644. /writePart9 {        % - writePart9 -
  645.   {
  646.     NonPageNs writeobjns
  647.   } (part9) writePart
  648. } bind def
  649.  
  650. % Write the main xref table and trailer.
  651. % Free variables: FirstPageXN.
  652. /writePart11xref {    % writePart11 -
  653.   {
  654.     0 FirstPageXN pdfwritexref
  655.   } (part11xref) writePart
  656. } bind def
  657. /writePart11rest {    % <part3start> writePart11rest -
  658.   {
  659.     << /Size FirstPageXN >> pdfwritetrailer
  660.     pdfwritestartxref
  661.   } (part11rest) writePart
  662. } bind def
  663.  
  664. % ---------------- Write hint tables ---------------- %
  665.  
  666. /bitsneeded {        % <maxvalue> bitsneeded <#bits>
  667.   0 exch { dup 0 eq { pop exit } if exch 1 add exch 2 idiv } loop
  668. } bind def
  669.  
  670. % Find the start and end of objects in the output.
  671. /omstart {        % <obj#> omstart <pos>
  672.   PDEBUG { (start\() print dup =only } if
  673.   omap
  674.   PDEBUG { (=>) print dup =only } if
  675.   XRef exch get
  676.   PDEBUG { (\) = ) print dup = } if
  677. } bind def
  678. /omend {        % <obj#> omend <pos>
  679.     % The end of an object is the start of the next object.
  680.     % The caller must be sure that this object is not the last one
  681.     % in part 9.
  682.   PDEBUG { (end\() print dup =only } if
  683.   omap
  684.   PDEBUG { (=>) print dup =only } if
  685.   1 add
  686.     % Check that the requested object wasn't the last one in part 6:
  687.     % the next object in the output file is the first in part 7.
  688.   PHSN omap 1 index eq { pop 1 } if
  689.   XRef exch get
  690.   PDEBUG { (\) = ) print dup = } if
  691. } bind def
  692. /omlength {        % <obj#> omlength <length>
  693.   dup omend exch omstart sub
  694. } bind def
  695.  
  696. % Find the Contents of a page.
  697. /contentsobjects {    % <pagedict> contentsobjects <firstobj#> <lastobj#>
  698.   /Contents get
  699.   dup oforce dup type /dicttype eq {
  700.     pop dup
  701.   } {
  702.     dup 0 get exch dup length 1 sub get
  703.   } ifelse
  704.   exch 0 get exch 0 get
  705. } bind def
  706. /contentsstart {    % <pagedict> contentsstart <pos>
  707.   contentsobjects pop omstart
  708. } bind def
  709. /contentslength {    % <pagedict> contentslength <length>
  710.   contentsobjects omend exch omstart sub
  711. } bind def
  712.  
  713.  
  714. /writePageOffsetHints {
  715.   PDEBUG { /writePageOffsetHints == } if
  716.   20 dict begin
  717.   /bits OFile bitstream def
  718.   /bwn { bits 3 1 roll bitwrite } def
  719.  
  720.     % Calculate least length of a page.
  721.   FirstPageLength OtherPageLengths { .min } forall
  722.   /minpl exch def
  723.     % Calculate least contents length.
  724.   FirstPageNs 0 get Objects exch lget contentslength
  725.   OtherPageNs { 0 get Objects exch lget contentslength .min } forall
  726.   /mincl exch def
  727.  
  728.     % The Adobe documentation says that all versions of Acrobat
  729.     % require item 8 (mincl) to be zero.  Patch this here.
  730.   /mincl 0 def
  731.  
  732.     % Calculate bits needed to represent greatest page length.
  733.   FirstPageLength OtherPageLengths { .max } forall
  734.   minpl sub bitsneeded /maxplbits exch def
  735.     % Calculate bits needed to represent the greatest Contents length.
  736.   FirstPageNs 0 get Objects exch lget contentslength
  737.   OtherPageNs { 0 get Objects exch lget contentslength .max } forall
  738.   mincl sub bitsneeded /maxclbits exch def
  739.  
  740.     % Per Adobe documentation, Acrobat requires that item 5 (maxplbits)
  741.     % be equal to item 9 (maxclbits).  Set both to the max of the two.
  742.   maxplbits maxclbits .max /maxplbits 1 index def /maxclbits exch def
  743.  
  744.         % 1: Least number of objects in a page:
  745.   FirstPageNs length OtherPageNs { length .min } forall
  746.   /minnop 1 index def 32 bwn
  747.         % 2: Location of first page's Page object:
  748.   FirstPageNs 0 get omap XRef exch get 32 bwn
  749.         % 3: Bits needed to represent greatest # of objects in a page:
  750.   FirstPageNs length OtherPageNs { length .max } forall
  751.   minnop sub bitsneeded /maxnopbits 1 index def 16 bwn
  752.         % 4: Least length of a page:
  753.   minpl 32 bwn
  754.         % 5: Bits needed to represent the greatest page length:
  755.   maxplbits 16 bwn
  756.         % 6: Least start of Contents offset:
  757.   0        % (Acrobat requires that this be 0.)
  758.   /minsco 1 index def 32 bwn
  759.         % 7: Bits needed to represent the greatest start of Contents
  760.         % offset:
  761.   0        % (Acrobat ignores this.)
  762.   /maxscobits 1 index def 16 bwn
  763.         % 8: Least contents length:
  764.   mincl 32 bwn
  765.         % 9: Bits needed to represent the greatest Contents length:
  766.   maxclbits 16 bwn
  767.         % 10: Bits needed to represent the greatest number of Shared
  768.   0        % Object references (we don't report any):
  769.   /maxsorbits 1 index def 16 bwn
  770.         % 11: Bits needed to identify a Shared Object (we don't
  771.   0        % report any):
  772.   /sobits 1 index def 16 bwn
  773.         % 12: Bits needed to represent numerator of fraction (only
  774.   0        % needed for Shared Object references, which we don't report):
  775.   /numfbits 1 index def 16 bwn
  776.         % 13: Denominator of fraction (only needed for Shared Object
  777.         % references, which we don't report):
  778.   255    % arbitrary
  779.   /denf 1 index def 16 bwn
  780.  
  781.         % Number of objects in pages:
  782.   FirstPageNs length minnop sub maxnopbits bwn
  783.   OtherPageNs {
  784.     length minnop sub maxnopbits bwn
  785.   } forall
  786.  
  787.         % Total length of pages in bytes;
  788.   FirstPageLength minpl sub maxplbits bwn
  789.   OtherPageLengths {
  790.     minpl sub maxplbits bwn
  791.   } forall
  792.  
  793.         % Number of shared objects referenced from page:
  794.         % (Currently we don't report this.)
  795.   OtherPageNs length 1 add { 0 maxsorbits bwn } repeat
  796.  
  797.         % Since there are no shared object references,
  798.         % the next two sections are empty.
  799.  
  800.         % Contents offsets:
  801.   [FirstPageNs OtherPageNs aload pop] {
  802.     0 get Objects exch lget contentsstart minsco sub maxscobits bwn
  803.   } forall
  804.  
  805.         % Contents lengths:
  806.   [FirstPageNs OtherPageNs aload pop] {
  807.     0 get Objects exch lget contentslength mincl sub maxclbits bwn
  808.   } forall
  809.  
  810.   bits bitflush end
  811. } bind def
  812.  
  813. /writeSharedObjectHints {
  814.   PDEBUG { /writeSharedObjectHints == } if
  815.   20 dict begin
  816.   /bits OFile bitstream def
  817.   /bwn { bits 3 1 roll bitwrite } def
  818.  
  819.         % Currently we use the Shared Object hint table only for
  820.         % the objects in the first page, which are all treated as
  821.         % "shared" objects.
  822.  
  823.         % Object number of first object in Shared Objects section
  824.         % (not currently used):
  825.   0 32 bwn
  826.         % Location of first object in Shared Objects section
  827.         % (not currently used): If there are no shared objects,
  828.         % Acrobat sets this to the location of linearization
  829.         % parameters object (the very first object).
  830.   { pdfwriteheader } tomemory length 32 bwn
  831.         % Number of Shared Object entries for first page:
  832.   FirstPageNs length 32 bwn
  833.         % Number of Shared Object entries for Shared Objects
  834.         % section (not currently used):
  835.   FirstPageNs length 32 bwn
  836.         % Bits needed to represent the greatest number of objects
  837.         % in a shared object group (always 0, because all groups
  838.         % have only 1 object):
  839.   0 16 bwn
  840.         % Least length of a Shared Object Group in bytes:
  841.   16#7fffffff FirstPageNs { omlength .min } forall
  842.   /minsol 1 index def 32 bwn
  843.         % Bits needed to represent the greatest length of a
  844.         % Shared Object Group:
  845.   0 FirstPageNs { omlength .max } forall
  846.   minsol sub bitsneeded
  847.   /maxsolbits 1 index def 16 bwn
  848.  
  849.         % Lengths of shared object groups:
  850.   FirstPageNs { omlength minsol sub maxsolbits bwn } forall
  851.  
  852.         % MD5 flag:
  853.   0 1 bwn
  854.  
  855.   bits bitflush end
  856. } bind def
  857.  
  858. % ---------------- Main program ---------------- %
  859.  
  860. /tmpprefix (/tmp/) def
  861.  
  862. /pdfOptimize {        % <infile> <outfile> pdfOptimize -
  863.   realtime 3 1 roll
  864.   exch pdfdict begin pdfopenfile dup begin
  865.   40 dict begin
  866.   /IDict exch def
  867.   /OFile exch def
  868.   /starttime exch def
  869.   /now {
  870.     QUIET { pop } { print (, t = ) print realtime starttime sub = flush } ifelse
  871.   } def
  872.   omapinit
  873.   
  874.     % Create and open a temporary file.
  875.  
  876.   null (w) .tempfile /TFile exch def /TFileName exch def
  877.   .setsafe
  878.  
  879.     % Read all objects into memory.
  880.  
  881.   Trailer touch
  882.   (Read objects) now
  883.  
  884.     % Replace indirect references to numbers.  This is needed
  885.     % for the Length of streams, and doesn't hurt anything else.
  886.  
  887.   replaceReferences
  888.   (Replaced references) now
  889.  
  890.     % Create the two new objects: the linearization parameter
  891.     % dictionary, and the Primary Hint Stream.
  892.  
  893.   /LPDict 10 dict def
  894.   /PHS 10 dict cvx def        % executable = stream
  895.   [LPDict PHS] createObjects
  896.   /LPDictN 1 index def 1 add
  897.   /PHSN exch def
  898.   PDEBUG { << /LPDictN LPDictN /PHSN PHSN >> === } if
  899.  
  900.     % Count the number of objects in the output.
  901.  
  902.   0 0 1 Objects llength 1 sub {
  903.     Generations exch lget 0 ne { 1 add } if
  904.   } for
  905.   /NObjects exch def
  906.   QUIET not { NObjects =only ( objects total) = flush } if
  907.  
  908.     % Propagate inherited attributes down the page tree.
  909.  
  910.   propagateAttributes
  911.   (Propagated attributes) now
  912.  
  913.     % Identify the document-level objects (part 4).
  914.  
  915.   identifyDocumentObjects /CatalogNs exch def
  916.   QUIET not { CatalogNs === flush } if
  917.   (Identified Catalog) now
  918.  
  919.       % Identify the first page's objects (part 6),
  920.     % including the Outlines tree if appropriate.
  921.  
  922.   pdfopencache
  923.   /FirstPageNs identifyFirstPageObjects def
  924.   QUIET not { FirstPageNs === flush } if
  925.   (Identified first page) now
  926.  
  927.     % Identify shared vs. non-shared objects for remaining pages
  928.     % (parts 7 and 8).
  929.  
  930.   identifyOtherPageObjects
  931.   /SharedNs exch def
  932.   /OtherPageNs exch def
  933.   QUIET not { OtherPageNs === flush SharedNs === flush } if
  934.   (Identified other pages) now
  935.  
  936.     % Identify objects not associated with any page (part 9).
  937.  
  938.   /NonPageNs identifyNonPageObjects def
  939.   QUIET not { NonPageNs === flush } if
  940.   (Identified non-pages) now
  941.  
  942.     % Assign final object numbers to all the objects.
  943.     % (The omap is currently empty.)
  944.  
  945.   /FirstPageXN assignObjectNumbers def
  946.   (Assigned objects #s) now
  947.  
  948.     % Write the document-level objects (part 4).
  949.  
  950.   { writePart4 } totemp
  951.   /CatalogTempEnd exch def /CatalogTempStart exch def
  952.   (Wrote Catalog) now
  953.  
  954.     % Write the first page's objects (part 6).
  955.  
  956.   { writePart6 } totemp
  957.   /FirstPageTempEnd exch def /FirstPageTempStart exch def
  958.   (Wrote first page) now
  959.  
  960.     % Write the non-shared objects for other pages (part 7).
  961.  
  962.   { writePart7 /OtherPageLengths exch def } totemp
  963.   /OtherPageTempEnd exch def /OtherPageTempStart exch def
  964.   (Wrote other pages) now
  965.  
  966.     % Write the shared objects for other pages (part 8).
  967.  
  968.   { writePart8 } totemp
  969.   /SharedTempEnd exch def /SharedTempStart exch def
  970.   (Wrote shared objects) now
  971.  
  972.     % Write the objects not associated with pages (part 9).
  973.  
  974.   { writePart9 } totemp
  975.   /NonPageTempEnd exch def /NonPageTempStart exch def
  976.  
  977.     % Compute conservative lengths of parts 2,3,5,11 of the output.
  978.     % It's OK for these to be too large, but not too small.
  979.  
  980.   % Make dummy XRef entres for LPDict and PHS.
  981.   XRef LPDictN omap 0 put
  982.   XRef PHSN omap 0 put
  983.  
  984.   /HeaderLength {    % this is exact
  985.     writePart1            % part 1
  986.   } tolength def
  987.   /CatalogLength    % this is exact
  988.     CatalogTempEnd CatalogTempStart sub def    % part 4
  989.   /FirstPageLength    % this is exact
  990.     FirstPageTempEnd FirstPageTempStart sub def    % part 6
  991.   /OtherObjectsLength    % this is exact
  992.     NonPageTempEnd OtherPageTempStart sub def    % parts 7,8,9
  993.   /ObjectsLength    % this is exact
  994.     CatalogLength FirstPageLength add OtherObjectsLength add def
  995.   /XrefLength {            % part 11
  996.     % The LPDict must end within the first 1024 bytes,
  997.     % so the start of the FirstPage xref table can't exceed 1024.
  998.     writePart11xref 1024 writePart11rest
  999.   } tolength def
  1000.   /NominalFileLength     % Make a generous allowance for parts 2,3,5.
  1001.     HeaderLength ObjectsLength 3 mul add 10000 add 99999 max def
  1002.   /FirstPageXrefLength {    % part 3
  1003.     NominalFileLength writePart3
  1004.   } tolength def
  1005.   /LPDictLength {        % part 2
  1006.     NominalFileLength dup 2 mul 2 copy add 1 index dup createLPDict writePart2
  1007.   } tolength def
  1008.  
  1009.     % Compute a few additional values from the above.
  1010.  
  1011.   /XrefBeginLength {
  1012.     (xref\n0 ) ows
  1013.     OFile FirstPageXN write=
  1014.   } tolength def
  1015.   HeaderLength LPDictLength add
  1016.   /FirstPageXrefStart 1 index def
  1017.   FirstPageXrefLength add
  1018.   /CatalogStart 1 index def
  1019.   CatalogLength add        % phsstart
  1020.   /PHSStart exch def
  1021.  
  1022.     % Adjust the object positions ignoring PHS.
  1023.     % (Writing the PHS needs these.)
  1024.  
  1025.   0 0 CatalogStart CatalogTempStart sub adjustObjectPositions
  1026.   % Make a temporary XRef entry for the PHS, for the benefit of omend.
  1027.   XRef PHSN omap CatalogStart put
  1028.   (Adjusted positions) now
  1029.  
  1030.     % Construct the hint tables (part 5).
  1031.  
  1032.   { writePageOffsetHints } totemp
  1033.   pop /PHSTempStart exch def
  1034.   { writeSharedObjectHints } totemp
  1035.   exch PHSTempStart sub PHS /S 3 -1 roll put
  1036.   PHSTempStart sub /PHSTempLength exch def
  1037.   (Wrote hints) now
  1038.  
  1039.   % Prepare to read TFile.
  1040.   TFile closefile
  1041.   /TFile TFileName (r) file def
  1042.  
  1043.   PHS
  1044.     dup /File TFile put
  1045.     dup /FilePosition PHSTempStart put
  1046.     dup /Length PHSTempLength put
  1047.   pop
  1048.   /PHSLength { writePart5 } tolength def
  1049.  
  1050.     % Construct the linearization parameter dictionary (part 2).
  1051.  
  1052.   PHSStart
  1053.   dup PHSLength add        % phsend
  1054.   /FirstPageStart 1 index def
  1055.   dup FirstPageLength add    % firstpageend
  1056.   dup OtherObjectsLength add
  1057.   /XrefStart 1 index def
  1058.   XrefBeginLength add        % xref0start
  1059.   dup XrefBeginLength sub XrefLength add    % fileend
  1060.     % Because of a bug, Acrobat Reader doesn't recognize any file
  1061.     % shorter than 1K as linearized.  Patch this here.
  1062.   1024 .max
  1063.   /FileLength 1 index def
  1064.   createLPDict
  1065.  
  1066.     % Adjust the object positions again, taking the PHS into account.
  1067.  
  1068.   PHSStart 0 PHSLength adjustObjectPositions
  1069.   (Readjusted positions) now
  1070.  
  1071.     % Finally, write the output file.
  1072.  
  1073.   writePart1
  1074.   writePart2
  1075.   FirstPageXrefStart padto
  1076.   XrefStart writePart3
  1077.   CatalogStart padto
  1078.   CatalogTempStart CatalogTempEnd copyrange    % part 4
  1079.   writePart5
  1080.   FirstPageStart padto
  1081.   FirstPageTempStart NonPageTempEnd copyrange    % parts 6,7,8,9
  1082.   % No Overflow Hint Stream (part 10).
  1083.   XrefStart padto
  1084.   writePart11xref
  1085.   { FirstPageXrefStart writePart11rest } tomemory
  1086.   FileLength 1 index length sub padto ows
  1087.   (Wrote output file) now
  1088.  
  1089.     % Wrap up.
  1090.  
  1091.   TFile closefile TFileName deletefile
  1092.   end        % temporary dict
  1093.   end        % IDict
  1094. } bind def
  1095.  
  1096. end            % pdfoptdict
  1097. .setglobal
  1098.  
  1099. % Check for command line arguments.
  1100. [ shellarguments {
  1101.   ] dup length 2 eq {
  1102.     % Load the pdfwrite utilities if necessary.
  1103.     /omapinit where { pop } { (pdfwrite.ps) runlibfile } ifelse
  1104.     save exch
  1105.     aload pop exch (r) file exch (w) file
  1106.     3000000 setvmthreshold
  1107.     pdfoptdict begin pdfOptimize end
  1108.     restore
  1109.   } {
  1110.     (Usage: gs -dNODISPLAY -- pdfopt.ps input.pdf output.pdf) = flush quit
  1111.   } ifelse
  1112. } {
  1113.   pop
  1114. } ifelse
  1115.